﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace FasterWPF
{
    //public abstract class TwoParamArgs
    //{
    //      public TwoParamArgs(int x, int y){}
    //}

    public enum ContainerType
    {
        HorizontalPanel = 0,
        Grid = 1,
        WrapPanel = 2,
        DockPanel = 3,
        VerticalPanel = 4,
        ContentControlPanel = 5,
        Canvas = 6,
        UniformGrid = 7
    }

    public static class CompositeTypeHelper
    { 
        public static IComposite CreateIComposite<T>(T parent)
    where T : FrameworkElement
        {
            if (parent.GetContainerType() == ContainerType.Grid)
            {
                return new GridComposite();
            }
            else if (parent.GetContainerType() == ContainerType.WrapPanel)
            {
                return new WrapPanelComposite();
            }
            else if (parent.GetContainerType() == ContainerType.DockPanel)
            {
                return new DockPanelComposite();
            }
            else if (parent.GetContainerType() == ContainerType.VerticalPanel)
            {
                return new VerticalPanelComposite();
            }
            else if (parent.GetContainerType() == ContainerType.ContentControlPanel)
            {
                return new ContentControlComposite();
            }
            else if (parent.GetContainerType() == ContainerType.Canvas)
            {
                return new CanvasComposite();
            }
            else if (parent.GetContainerType() == ContainerType.UniformGrid)
            {
                return new UniformGridComposite();
            }
            else  //ContainerType.HorizontalPanel
            {
                return new HorizontalPanelComposite();
            }
        }

        public static IComposite GetICompositeFromChild(FrameworkElement child)
        {    
            if ((((FrameworkElement)child).Parent) is Grid)
            {
                return new GridComposite();
            }
            else if ((((FrameworkElement)child).Parent) is WrapPanel)
            {
                return new WrapPanelComposite();
            }
            else if ((((FrameworkElement)child).Parent) is DockPanel)
            {
                return new DockPanelComposite();
            }
            else if (((((FrameworkElement)child).Parent) is StackPanel)  && (((((FrameworkElement)child).Parent) as StackPanel).Orientation==Orientation.Vertical))
            {
                return new VerticalPanelComposite();
            }
            else if (((((FrameworkElement)child).Parent) is ContentControlPanel))
            {
                return new ContentControlComposite();
            }
            else if (((((FrameworkElement)child).Parent) is UniformGrid))
            {
                return new UniformGridComposite();
            }
            else if (((((FrameworkElement)child).Parent) is Canvas))
            {
                return new CanvasComposite();
            }
            else  //ContainerType.HorizontalPanel
            {
                return new HorizontalPanelComposite();
            }
        }

        public static void ApplySettingsForChild<K>(Dictionary<string, object> settings, K objToAdd) 
            where K : FrameworkElement, new()
        {
            string prefix = objToAdd.GetType().Name.ToString();

            foreach (PropertyInfo propInf in objToAdd.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                object value = null;
                if (settings.TryGetValue((prefix + propInf.Name), out value))
                {
                    if (propInf.CanWrite)
                    {
                        Type settingType = propInf.PropertyType;
                        Caster caster = new Caster();
                        propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value }), null);
                    }
                }
            }
        }

        public static void ApplySettingsForChildAtRowCol<K>(Dictionary<string, object> settings, int row, int col, K objToAdd) where K : FrameworkElement, new()
        {
            string prefix = objToAdd.GetType().Name.ToString();
            string row_col = "_" + row + "_" + col;

            foreach (PropertyInfo propInf in objToAdd.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                object value = null;
                if (settings.TryGetValue((prefix + propInf.Name + row_col), out value))
                {
                    if (propInf.CanWrite)
                    {
                        Type settingType = propInf.PropertyType;
                        Caster caster = new Caster();
                        propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value }), null);
                    }
                }
            }
            
            //get base type 1 level deep
            string prefix2 = null;

            if (objToAdd.GetType().BaseType != null)
            {
                prefix2 = objToAdd.GetType().BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value2 = null;
                    if (settings.TryGetValue((prefix2 + propInf.Name + row_col), out value2))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value2 }), null);
                        }
                    }
                }
            }

            //get base type 2 level deep
            string prefix3 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null)
            {
                prefix3 = objToAdd.GetType().BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value3 = null;
                    if (settings.TryGetValue((prefix3 + propInf.Name + row_col), out value3))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value3 }), null);
                        }
                    }
                }
            }

            //get base type 3 level deep
            string prefix4 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null)
            {
                prefix4 = objToAdd.GetType().BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value4 = null;
                    if (settings.TryGetValue((prefix4 + propInf.Name + row_col), out value4))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value4 }), null);
                        }
                    }
                }
            }

            //get base type 4 level deep
            string prefix5 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix5 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value5 = null;
                    if (settings.TryGetValue((prefix5 + propInf.Name + row_col), out value5))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value5 }), null);
                        }
                    }
                }
            }

            //get base type 5 level deep
            string prefix6 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix6 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value6 = null;
                    if (settings.TryGetValue((prefix6 + propInf.Name + row_col), out value6))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value6 }), null);
                        }
                    }
                }
            }

            //get base type 6 level deep
            string prefix7 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix7 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value7 = null;
                    if (settings.TryGetValue((prefix7 + propInf.Name + row_col), out value7))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value7 }), null);
                        }
                    }
                }
            }

            //get base type 7 level deep
            string prefix8 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix8 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value8 = null;
                    if (settings.TryGetValue((prefix8 + propInf.Name + row_col), out value8))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value8 }), null);
                        }
                    }
                }
            }

            //get base type 8 level deep
            string prefix9 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix9 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (PropertyInfo propInf in objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    object value9 = null;
                    if (settings.TryGetValue((prefix9 + propInf.Name + row_col), out value9))
                    {
                        if (propInf.CanWrite)
                        {
                            Type settingType = propInf.PropertyType;
                            Caster caster = new Caster();
                            propInf.SetValue(objToAdd, caster.GetGenericMethod(settingType).Invoke(null, new object[] { value9 }), null);
                        }
                    }
                }
            }
        }

        public static void ApplyAnyAttachedProperties<T, K>(T lbx, Dictionary<string, object> settings, K objToAdd)
            where T : FrameworkElement
            where K : FrameworkElement, new()
        {
            Dictionary<string, object> attachDict = lbx.GetAttachedPropertiesDictionary();
            string prefix = objToAdd.GetType().Name.ToString();

            foreach (KeyValuePair<string, object> key in attachDict)
            {
                if ((Helpers.Left(key.Key, prefix.Length) == prefix))
                {
                    if (key.Value is Action<K>)
                    {
                        ((Action<K>)key.Value)(objToAdd);
                    }
                }
            }
        }
        
        public static void ApplyAnyAttachedPropertiesByRowCol<T, K>(T lbx, Dictionary<string, object> settings, int row, int col, K objToAdd)
            where T : FrameworkElement
            where K : FrameworkElement, new()
        {
            string row_col = "_" + row + "_" + col;
            char[] underScore = new char[] { '_' };

            //e.g. key:  Label ColumnSpan _5_3  type property name row col

            Dictionary<string, object> attachDict = lbx.GetAttachedPropertiesByRowColDictionary();
            string prefix = objToAdd.GetType().Name.ToString();

            foreach (KeyValuePair<string, object> key in attachDict)
            {
                if (  (Helpers.Left(key.Key, prefix.Length)==prefix) && ((Int32.Parse(key.Key.Split(underScore)[1]))==row) && ((Int32.Parse(key.Key.Split(underScore)[2]))==col))
                {
                    if (key.Value is Action<K>)
                    {
                        ((Action<K>)key.Value)(objToAdd);
                    }
                }
            }

            //get base type 1 level deep
            string prefix2 = null;

            if (objToAdd.GetType().BaseType != null)
            {
                prefix2 = objToAdd.GetType().BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix2.Length) == prefix2) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }
            
            //get base type 2 level deep
            string prefix3 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null)
            {
                prefix3 = objToAdd.GetType().BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix3.Length) == prefix3) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }
            
            //get base type 3 level deep
            string prefix4 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null)
            {
                prefix4 = objToAdd.GetType().BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix4.Length) == prefix4) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }
            
            //get base type 4 level deep
            string prefix5 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix5 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix5.Length) == prefix5) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }

            //get base type 5 level deep
            string prefix6 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix6 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix6.Length) == prefix6) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }

            //get base type 6 level deep
            string prefix7 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix7 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix7.Length) == prefix7) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }

            //get base type 7 level deep
            string prefix8 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix8 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix8.Length) == prefix8) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }

            //get base type 8 level deep
            string prefix9 = null;

            if (objToAdd.GetType().BaseType != null && objToAdd.GetType().BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null && objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType != null)
            {
                prefix9 = objToAdd.GetType().BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.BaseType.Name.ToString();

                foreach (KeyValuePair<string, object> key in attachDict)
                {
                    if ((Helpers.Left(key.Key, prefix9.Length) == prefix9) && ((Int32.Parse(key.Key.Split(underScore)[1])) == row) && ((Int32.Parse(key.Key.Split(underScore)[2])) == col))
                    {
                        if (key.Value is Action<K>)
                        {
                            ((Action<K>)key.Value)(objToAdd);
                        }
                    }
                }
            }
            
        }
    }//end of class
}//end of namespace
